package com.myk.game.gobangchess.rooms.absrooms;

import com.myk.game.gobangchess.CustomBottom;
import com.myk.game.gobangchess.Player;
import com.myk.game.gobangchess.constants.ColorCost;
import com.myk.game.gobangchess.rooms.absrooms.movechess.MoreColorCost;
import com.myk.game.gobangchess.signs.*;
import com.myk.game.gobangchess.utils.GridsUtils;

import java.util.*;

public abstract class AbsMultiViewChessRoom extends AbsMultiViewRoom {
    public Map<Integer, Side> sideMap = new HashMap<>();
    public List<Integer> newPutIndex = new ArrayList<>(); // 落子，释放技能的标记，双方都能看见

    public AbsMultiViewChessRoom(String roomName, String roomIntroduce, String chessName, int[][] curGrids, boolean isSupportPersonal, boolean isSaveHistoryToFile,String ... modes) {
        super(roomName, roomIntroduce, chessName, curGrids, isSupportPersonal, isSaveHistoryToFile,modes);
    }

    // ******************************** Room 相关

    @Override
    public void resetGrids() {
        step = 0;
        gameTip = "-1";
        curGrids = GridsUtils.copyGrids(originGrids);
        signList.clear();
        sideMap.clear();
        newPutIndex.clear();
        initSides();
    }

    @Override
    public String getGameInfo(int view) {
        //画一下对弈双方
        for (Side side : sideMap.values()) {
            side.render(this, view);
        }
        //画一下落子记号
        for (int index : newPutIndex) {
            signList.add(new GroundSign(index, MoreColorCost.NO_COLOR, MoreColorCost.PUT_ED, 2, 0, 0));
        }
        return super.getGameInfo();
    }

    public int getStep() {
        return step;
    }

    /**
     * 生成初始化棋子
     */
    public abstract void initSides();

    /**
     * 手动选择先行棋方
     */
    public abstract void noticeFirstMove();

    /**
     * 切换下棋方
     */
    public void noticeFinishStep() {
        stepAdd(); //增加步数避免意外重置棋盘
        if (cur == 1) {
            noticeWhiteMove();
        } else {
            noticeBlackMove();
        }
    }

    // ******************************** Side 相关

    /**
     * 生成局面信息
     */
    public String info() {
        List<String> sideInfo = new ArrayList<>();
        for (Side side : sideMap.values()) {
            sideInfo.add(side.info());
        }
        return String.join("\n", sideInfo);
    }

    /**
     * 清理对弈一方的提示标记
     */
    public void clearSideSign() {
        for (Side side : sideMap.values()) {
            side.clearSign();
        }
    }

    /**
     * 设置全部布局完成
     */
    public void setAllSideSet() {
        for (Side s : sideMap.values()) {
            s.finishSettingUp(this, null);
        }
    }

    /**
     * 判断是否全部布局完成
     *
     * @return 是否全部布局完成
     */
    public boolean getAllSideSet() {
        for (Side s : sideMap.values()) {
            if (s.settingUpBoard) {
                return false;
            }
        }
        return true;
    }

    // ******************************** Chess 相关

    /**
     * 所有棋子列表
     */
    public List<Chess> chessList() {
        List<Chess> c = new ArrayList<>();
        for (Side side : sideMap.values()) {
            c.addAll(side.chessList);
        }
        return c;
    }

    /**
     * 根据坐标搜索棋子
     */
    public Chess getChessByPos(int row, int col) {
        for (Side side : sideMap.values()) {
            Chess c = side.getChessByPos(row, col);
            if (c != null) {
                return c;
            }
        }
        return null;
    }

    public void removeChess(Chess chess) {
        for (Side side : sideMap.values()) {
            side.chessList.remove(chess);
        }
    }

    public Map<Integer, Chess> getChessMap() {
        Map<Integer, Chess> indexes = new HashMap<>();
        for (Chess chess : chessList()) {
            indexes.put(getIndex(chess.pRow, chess.pCol), chess);
        }
        return indexes;
    }

    /**
     * 根据index搜索棋子
     */
    public Chess getChessByIndex(int index) {
        int[] pos = getPositionByIndex(index);
        return getChessByPos(pos[0], pos[1]);
    }

    /**
     * 棋子的各种信息
     */
    public abstract static class Chess {
        public String mName; //棋子简名
        public int mSide; //归属黑棋还是白棋 1黑  2白
        public int pRow; //在棋盘上的横坐标
        public int pCol; //在棋盘上的纵坐标

        public Chess(String name, int mSide, int pRow, int pCol) {
            this.mName = name;
            this.mSide = mSide;
            this.pRow = pRow;
            this.pCol = pCol;
        }

        protected String getColor() {
            return mSide == 1 ? MoreColorCost.BLACK_CHESS_WORD : MoreColorCost.WHITE_CHESS_WORD;
        }

        protected String getBgColor() {
            return mSide == 1 ? ColorCost.DETAINED_BADGE_WHITE : ColorCost.DETAINED_BADGE_BLACK;
        }

        public void render(AbsMultiViewChessRoom room, int view) {
            room.curGrids[pRow][pCol] = mSide; //显示棋子的底座
            room.signList.add(new TextSign(room.getIndex(pRow, pCol), mName, getColor()));
        }

        public String info() {
            return String.format("%s-%d/%d", mName, pRow, pCol);
        }

        public String sideString() {
            return mSide == 1 ? "黑方" : "白方";
        }

        /**
         * 更新自己的位置
         */
        public void move(AbsMultiViewChessRoom room, int row, int col) {
            pRow = row;
            pCol = col;
        }

        /**
         * 走子后判断输赢
         *
         * @param room 房间信息
         * @return 1 己方获胜，-1 己方落败，0 无事发生
         */
        public int checkWinAfterMove(AbsMultiViewChessRoom room) {
            return 0;
        }

        /**
         * 吃子后判断输赢
         *
         * @param room     房间信息
         * @param chessAte 被吃的棋子
         * @return 1 己方获胜，-1 己方落败，0 无事发生
         */
        public int checkWinAfterEat(AbsMultiViewChessRoom room, Chess chessAte) {
            return 0;
        }

        /**
         * 尝试移动，返回移动后的位置，移动后的位置不一定是希望移动的位置
         */
        public int[] tryMove(AbsMultiViewChessRoom room, int row, int col) {
            List<Integer> moveRange = this.moveRange(room);
            if (moveRange != null && moveRange.contains(room.getIndex(row, col))) {
                return new int[]{row, col};
            }
            List<Integer> eatRange = this.eatRange(room);
            if (eatRange != null && eatRange.contains(room.getIndex(row, col))) {
                return new int[]{row, col};
            }
            return null;
        }

        protected List<Integer> genMoveRange(AbsMultiViewChessRoom room, int[][] dxdys, int distance) {
            return genMoveRange(room, dxdys, distance, 0, 0);
        }

        protected List<Integer> genMoveRange(AbsMultiViewChessRoom room, int[][] dxdys, int distance, int canJump, int mustJump) {
            List<Integer> rangeIndex = new ArrayList<>();
            for (int[] dxdy : dxdys) {
                int skip = 0;
                for (int x = pRow + dxdy[0], y = pCol + dxdy[1], i = 0; x >= 0 && x < room.curGrids.length && y >= 0 && y < room.curGrids[0].length && i < distance; x += dxdy[0], y += dxdy[1], i++) {
                    Chess chess = room.getChessByPos(x, y);
                    if (chess != null) {
                        skip++;
                        if (skip > canJump) {
                            break;
                        }
                        continue;
                    }
                    if (skip >= mustJump) {
                        rangeIndex.add(room.getIndex(x, y));
                    }
                }
            }
            return rangeIndex;
        }

        public List<Integer> genEatRange(AbsMultiViewChessRoom room, int[][] dxdys, int distance) {
            return genEatRange(room, dxdys, distance, 0, 0);
        }

        protected List<Integer> genEatRange(AbsMultiViewChessRoom room, int[][] dxdys, int distance, int canJump, int mustJump) {
            List<Integer> rangeIndex = new ArrayList<>();
            for (int[] dxdy : dxdys) {
                int skip = 0;
                for (int x = pRow + dxdy[0], y = pCol + dxdy[1], i = 0; x >= 0 && x < room.curGrids.length && y >= 0 && y < room.curGrids[0].length && i < distance; x += dxdy[0], y += dxdy[1], i++) {
                    Chess chess = room.getChessByPos(x, y);
                    if (chess != null) {
                        if (chess.mSide != mSide && skip >= mustJump) {
                            rangeIndex.add(room.getIndex(x, y));
                        }
                        skip++;
                        if (skip > canJump) {
                            break;
                        }
                    }
                }
            }
            return rangeIndex;
        }

        public abstract List<Integer> moveRange(AbsMultiViewChessRoom room);

        public abstract List<Integer> eatRange(AbsMultiViewChessRoom room);
    }

    /**
     * 对弈双方的各种信息
     */
    public abstract static class Side {
        public int mSide;//归属黑棋还是白棋 1黑  2白
        public boolean settingUpBoard = false; //布局状态，布局完成转为true
        public Chess selectedTarget; //选中的目标，准备交换/准备走棋
        public List<Chess> chessList = new ArrayList<>(); // 对弈一方的所有棋子
        public Map<Integer, String> rangeIndex = new HashMap<>(); // 移动/吃子/技能范围，这一部分只有当前玩家可见
        public int[] moveArray = null; // 走子标记

        public Side(int side) {
            this.mSide = side;
        }

        public void render(AbsMultiViewChessRoom room, int view) {
            // 己方视角或已全部完成布局
            if ((view & mSide) > 0 || room.getAllSideSet()) {
                //把每个棋子画上
                for (Chess chess : chessList) {
                    chess.render(room, view);
                }
            }
            if ((view & mSide) > 0) {
                //画一下选中的棋子
                if (selectedTarget != null) {
                    int index = room.getIndex(selectedTarget.pRow, selectedTarget.pCol);
                    room.signList.add(new GroundSign(index, MoreColorCost.NO_COLOR, MoreColorCost.SELECT_ED, 2, 0, 0));
                }
                //画一下可移动范围
                for (Map.Entry<Integer, String> entry : rangeIndex.entrySet()) {
                    room.signList.add(new ColorSign(entry.getKey(), entry.getValue()));
                }
            }
            if ((view & mSide) > 0 && settingUpBoard) {
                List<Integer> swapRange = this.swapRange(room);
                room.signList.add(new AroundSign(swapRange, MoreColorCost.SWAP_BG, MoreColorCost.NO_COLOR, 1));
            }
            //画一下箭头
            if (moveArray != null) {
                room.signList.add(new LineSign(moveArray[0], moveArray[1], ColorCost.MOVE_ARROW, 1));
            }
        }

        public String sideString() {
            return mSide == 1 ? "黑方" : "白方";
        }

        public String info() {
            ArrayList<String> chessInfo = new ArrayList<>();
            for (Chess chess : chessList) {
                chessInfo.add(chess.info());
            }
            Collections.sort(chessInfo);
            return String.join(",", chessInfo);
        }

        public void startSettingUp(AbsMultiViewChessRoom room, Player player0) {
            this.settingUpBoard = true;
            room.gameTip = "请进行布局，可以随意交换布局范围内的棋子";
            player0.registerCustomBottom(
                    new CustomBottom(room, "完成布局", player -> {
                        finishSettingUp(room, player0);
                        player0.clearCustomBottom();
                        player0.dispatchCustomBottom();
                    })
            );
            room.noticeAllMove();
        }

        public void finishSettingUp(AbsMultiViewChessRoom room, Player player) {
            if (!settingUpBoard) {
                return;
            }
            this.settingUpBoard = false;
            this.selectedTarget = null;
            if (room.player_black == room.player_white) {
                room.setAllSideSet();
            }
            if (room.getAllSideSet()) {
                room.noticeFirstMove();
                room.gameTip = "开始对弈，白方先行";
            }
        }

        public void clearSign() {
            rangeIndex.clear();
            moveArray = null;
        }

        /**
         * 根据坐标搜索棋子
         */
        public Chess getChessByPos(int row, int col) {
            for (Chess chess : chessList) {
                if (chess.pRow == row && chess.pCol == col) {
                    return chess;
                }
            }
            return null;
        }

        /**
         * 选中或移动
         *
         * @param row 行位置
         * @param col 列位置
         */
        public void selectOrMove(AbsMultiViewChessRoom room, int row, int col) {
            Chess target = getChessByPos(row, col);
            if (target == null && selectedTarget == null) {
                // 既没有点击棋子，又没有已选中的棋子
                return;
            }
            if (selectedTarget == null) {
                // 选中棋子
                selectedTarget = target;
                for (int index : target.moveRange(room)) {
                    rangeIndex.put(index, MoreColorCost.MOVE);
                }
                for (int index : target.eatRange(room)) {
                    rangeIndex.put(index, MoreColorCost.EAT);
                }
                room.noticeAllRefreshGameInfo();//通知界面刷新
                return;
            }
            if (selectedTarget == target) {
                //取消选中棋子
                selectedTarget = null;
                rangeIndex.clear();
                room.noticeAllRefreshGameInfo();
                return;
            }
            // 尝试走子
            int startIndex = room.getIndex(selectedTarget.pRow, selectedTarget.pCol);
            int[] pos = selectedTarget.tryMove(room, row, col);
            if (pos == null){
                //不能走子，取消选中棋子
                selectedTarget = null;
                rangeIndex.clear();
                room.noticeAllRefreshGameInfo();
                return;
            }
            //走子逻辑
            int winValue;
            Chess chessBeAte = room.getChessByPos(pos[0], pos[1]);
            if (chessBeAte != null && !canDoEat(room)) {
                room.gameTip = "先手第一回合不能吃子";
                room.noticeAllRefreshGameInfo();
                return;
            } else {
                room.gameTip = "正常对局中。。。";
            }
            selectedTarget.move(room, pos[0], pos[1]);
            if (chessBeAte != null) {
                room.removeChess(chessBeAte);
                winValue = selectedTarget.checkWinAfterEat(room, chessBeAte);
                if (winValue < 0) {
                    room.doOver(3 - selectedTarget.mSide, selectedTarget.sideString() + selectedTarget.mName + "吃掉" + chessBeAte.sideString() + chessBeAte.mName + "，" + selectedTarget.sideString() + "落败");
                    return;
                } else if (winValue > 0) {
                    room.doOver(selectedTarget.mSide, selectedTarget.sideString() + selectedTarget.mName + "吃掉" + chessBeAte.sideString() + chessBeAte.mName + "，" + selectedTarget.sideString() + "获胜");
                    return;
                }
            }
            winValue = selectedTarget.checkWinAfterMove(room);
            if (winValue < 0) {
                room.doOver(3 - selectedTarget.mSide, selectedTarget.sideString() + selectedTarget.mName + "移动后达成落败条件");
                return;
            } else if (winValue > 0) {
                room.doOver(selectedTarget.mSide, selectedTarget.sideString() + selectedTarget.mName + "移动后达成获胜条件");
                return;
            }
            selectedTarget = null;
            room.clearSideSign();
            moveArray = new int[]{startIndex, room.getIndex(pos[0], pos[1])};
            room.noticeFinishStep();
        }

        /**
         * 选中或交换
         *
         * @param row 行位置
         * @param col 列位置
         */
        public void selectOrSwap(AbsMultiViewChessRoom room, int row, int col) {
            int index = room.getIndex(row, col);
            List<Integer> swapRange = this.swapRange(room);
            if (swapRange == null || !swapRange.contains(index)) {
                return;
            }
            Chess target = getChessByPos(row, col);
            if (selectedTarget == null) {
                selectedTarget = target;
                return;
            }
            int tempCol = selectedTarget.pCol;
            int tempRow = selectedTarget.pRow;
            selectedTarget.pRow = row;
            selectedTarget.pCol = col;
            if (target != null) {
                target.pRow = tempRow;
                target.pCol = tempCol;
            }
            selectedTarget = null;
        }

        public abstract List<Integer> swapRange(AbsMultiViewChessRoom room);
        public abstract boolean canDoEat(AbsMultiViewChessRoom room);
    }

}
